/*-------------------------------------------------------------------------
 *
 * nodeTableFuncscan.c
 *	  扫描RangeTableFunc（XMLTABLE类似函数）的支持例程。
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/executor/nodeTableFuncscan.c
 *
 *-------------------------------------------------------------------------
 */
/*
 * 接口例程
 *		ExecTableFuncScan		扫描一个函数。
 *		ExecFunctionNext		以顺序顺序检索下一个元组。
 *		ExecInitTableFuncScan	创建并初始化一个 TableFuncscan 节点。
 *		ExecEndTableFuncScan		释放分配的任何存储。
 *		ExecReScanTableFuncScan	重新扫描函数
 */
#include "postgres.h"

#include "executor/executor.h"
#include "executor/nodeTableFuncscan.h"
#include "executor/tablefunc.h"
#include "miscadmin.h"
#include "nodes/execnodes.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/xml.h"

static TupleTableSlot *fc_TableFuncNext(TableFuncScanState *fc_node);
static bool fc_TableFuncRecheck(TableFuncScanState *fc_node, TupleTableSlot *fc_slot);

static void fc_tfuncFetchRows(TableFuncScanState *fc_tstate, ExprContext *fc_econtext);
static void fc_tfuncInitialize(TableFuncScanState *fc_tstate, ExprContext *fc_econtext, Datum fc_doc);
static void fc_tfuncLoadRows(TableFuncScanState *fc_tstate, ExprContext *fc_econtext);

/* ----------------------------------------------------------------
 *						扫描支持
 * ----------------------------------------------------------------
 */
/* ----------------------------------------------------------------
 *		TableFuncNext
 *
 *		这是 ExecTableFuncScan 的主要部分
 * ----------------------------------------------------------------
 */
static TupleTableSlot * fc_TableFuncNext(TableFuncScanState *fc_node)
{
	TupleTableSlot *fc_scanslot;

	fc_scanslot = fc_node->ss.ss_ScanTupleSlot;

	/*
	 * 如果第一次通过，则从函数读取所有元组并将它们放入
	 * tuplestore。后续调用只从 tuplestore 获取元组。
	 */
	if (fc_node->tupstore == NULL)
		fc_tfuncFetchRows(fc_node, fc_node->ss.ps.ps_ExprContext);

	/*
	 * 从 tuplestore 中获取下一个元组。
	 */
	(void) tuplestore_gettupleslot(fc_node->tupstore,
								   true,
								   false,
								   fc_scanslot);
	return fc_scanslot;
}


static bool fc_TableFuncRecheck(TableFuncScanState *fc_node, TupleTableSlot *fc_slot)
{
	
	return true;
}

/* ----------------------------------------------------------------
 * ExecTableFuncScan(node)
 *
 * 依次扫描函数并返回下一个符合条件的元组。
 * 我们调用 ExecScan() 例程并传递适当的访问方法函数。
 * ----------------------------------------------------------------
 */
static TupleTableSlot * fc_ExecTableFuncScan(PlanState *fc_pstate)
{
	TableFuncScanState *fc_node = castNode(TableFuncScanState, fc_pstate);

	return ExecScan(&fc_node->ss,
					(ExecScanAccessMtd) fc_TableFuncNext,
					(ExecScanRecheckMtd) fc_TableFuncRecheck);
}

/* ----------------------------------------------------------------
 * ExecInitTableFuncScan
 * ----------------------------------------------------------------
 */
TableFuncScanState *
ExecInitTableFuncScan(TableFuncScan *fc_node, EState *fc_estate, int fc_eflags)
{
	TableFuncScanState *fc_scanstate;
	TableFunc  *fc_tf = fc_node->tablefunc;
	TupleDesc	fc_tupdesc;
	int			fc_i;

	/* 检查不支持的标志 */
	Assert(!(fc_eflags & EXEC_FLAG_MARK));

	/*
	 * TableFuncscan 不应该有任何子节点。
	 */
	Assert(outerPlan(fc_node) == NULL);
	Assert(innerPlan(fc_node) == NULL);

	/*
	 * 为节点创建新的 ScanState
	 */
	fc_scanstate = makeNode(TableFuncScanState);
	fc_scanstate->ss.ps.plan = (Plan *) fc_node;
	fc_scanstate->ss.ps.state = fc_estate;
	fc_scanstate->ss.ps.ExecProcNode = fc_ExecTableFuncScan;

	/*
	 * 杂项初始化
	 *
	 * 为节点创建表达式上下文
	 */
	ExecAssignExprContext(fc_estate, &fc_scanstate->ss.ps);

	/*
	 * 初始化源元组类型
	 */
	fc_tupdesc = BuildDescFromLists(fc_tf->colnames,
								 fc_tf->coltypes,
								 fc_tf->coltypmods,
								 fc_tf->colcollations);
	/* 及相应的扫描槽 */
	ExecInitScanTupleSlot(fc_estate, &fc_scanstate->ss, fc_tupdesc,
						  &TTSOpsMinimalTuple);

	/*
	 * 初始化结果类型和投影。
	 */
	ExecInitResultTypeTL(&fc_scanstate->ss.ps);
	ExecAssignScanProjectionInfo(&fc_scanstate->ss);

	/*
	 * 初始化子表达式
	 */
	fc_scanstate->ss.ps.qual =
		ExecInitQual(fc_node->scan.plan.qual, &fc_scanstate->ss.ps);

	/* 目前只支持 XMLTABLE */
	fc_scanstate->routine = &XmlTableRoutine;

	fc_scanstate->perTableCxt =
		AllocSetContextCreate(CurrentMemoryContext,
							  "TableFunc per value context",
							  ALLOCSET_DEFAULT_SIZES);
	fc_scanstate->opaque = NULL;	/* 在运行时初始化 */

	fc_scanstate->ns_names = fc_tf->ns_names;

	fc_scanstate->ns_uris =
		ExecInitExprList(fc_tf->ns_uris, (PlanState *) fc_scanstate);
	fc_scanstate->docexpr =
		ExecInitExpr((Expr *) fc_tf->docexpr, (PlanState *) fc_scanstate);
	fc_scanstate->rowexpr =
		ExecInitExpr((Expr *) fc_tf->rowexpr, (PlanState *) fc_scanstate);
	fc_scanstate->colexprs =
		ExecInitExprList(fc_tf->colexprs, (PlanState *) fc_scanstate);
	fc_scanstate->coldefexprs =
		ExecInitExprList(fc_tf->coldefexprs, (PlanState *) fc_scanstate);

	fc_scanstate->notnulls = fc_tf->notnulls;

	/* 这些现在分配，稍后初始化 */
	fc_scanstate->in_functions = palloc(sizeof(FmgrInfo) * fc_tupdesc->natts);
	fc_scanstate->typioparams = palloc(sizeof(Oid) * fc_tupdesc->natts);

	/*
	 * 填充必要的 fmgr 信息。
	 */
	for (fc_i = 0; fc_i < fc_tupdesc->natts; fc_i++)
	{
		Oid			fc_in_funcid;

		getTypeInputInfo(TupleDescAttr(fc_tupdesc, fc_i)->atttypid,
						 &fc_in_funcid, &fc_scanstate->typioparams[fc_i]);
		fmgr_info(fc_in_funcid, &fc_scanstate->in_functions[fc_i]);
	}

	return fc_scanstate;
}

/* ----------------------------------------------------------------
 * ExecEndTableFuncScan
 *
 * 释放通过 C 例程分配的任何存储。
 * ----------------------------------------------------------------
 */
void ExecEndTableFuncScan(TableFuncScanState *fc_node)
{
	/*
	 * 释放表达式上下文
	 */
	ExecFreeExprContext(&fc_node->ss.ps);

	/*
	 * 清空元组表
	 */
	if (fc_node->ss.ps.ps_ResultTupleSlot)
		ExecClearTuple(fc_node->ss.ps.ps_ResultTupleSlot);
	ExecClearTuple(fc_node->ss.ss_ScanTupleSlot);

	/*
	 * 释放元组存储资源
	 */
	if (fc_node->tupstore != NULL)
		tuplestore_end(fc_node->tupstore);
	fc_node->tupstore = NULL;
}

/* ----------------------------------------------------------------
 * ExecReScanTableFuncScan
 *
 * 重新扫描关系。
 * ----------------------------------------------------------------
 */
void ExecReScanTableFuncScan(TableFuncScanState *fc_node)
{
	Bitmapset  *fc_chgparam = fc_node->ss.ps.chgParam;

	if (fc_node->ss.ps.ps_ResultTupleSlot)
		ExecClearTuple(fc_node->ss.ps.ps_ResultTupleSlot);
	ExecScanReScan(&fc_node->ss);

	/*
	 * 在更改参数时重新计算。
	 */
	if (fc_chgparam)
	{
		if (fc_node->tupstore != NULL)
		{
			tuplestore_end(fc_node->tupstore);
			fc_node->tupstore = NULL;
		}
	}

	if (fc_node->tupstore != NULL)
		tuplestore_rescan(fc_node->tupstore);
}

/* ----------------------------------------------------------------
 * tfuncFetchRows
 *
 * 从 TableFunc 生产者读取行
 * ----------------------------------------------------------------
 */
static void fc_tfuncFetchRows(TableFuncScanState *fc_tstate, ExprContext *fc_econtext)
{
	const TableFuncRoutine *fc_routine = fc_tstate->routine;
	MemoryContext fc_oldcxt;
	Datum		fc_value;
	bool		fc_isnull;

	Assert(fc_tstate->opaque == NULL);

	/* 为结果构建元组存储 */
	fc_oldcxt = MemoryContextSwitchTo(fc_econtext->ecxt_per_query_memory);
	fc_tstate->tupstore = tuplestore_begin_heap(false, false, work_mem);

	/*
	 * 每次调用以获取新的一组行 - 如果在横向连接中使用 XMLTABLE，可能会有很多行 -
	 * 将分配可能相当大的内存，因此我们无法在这里使用每查询上下文。
	 * perTableCxt 现在的功能与 FunctionScan 中的 "argcontext" 相同 -
	 * 存储每次调用（即一个结果表）生命周期数据的地方
	 * （与每查询或每结果元组相对）。
	 */
	MemoryContextSwitchTo(fc_tstate->perTableCxt);

	PG_TRY();
	{
		fc_routine->InitOpaque(fc_tstate,
							fc_tstate->ss.ss_ScanTupleSlot->tts_tupleDescriptor->natts);

		/*
		 * 如果评估文档表达式返回 NULL，表表达式为空，我们立即返回。
		 */
		fc_value = ExecEvalExpr(fc_tstate->docexpr, fc_econtext, &fc_isnull);

		if (!fc_isnull)
		{
			/* 否则，将文档值传递给表构建器 */
			fc_tfuncInitialize(fc_tstate, fc_econtext, fc_value);

			/* 初始化序数计数器 */
			fc_tstate->ordinal = 1;

			/* 加载所有行到元组存储中，我们完成了 */
			fc_tfuncLoadRows(fc_tstate, fc_econtext);
		}
	}
	PG_CATCH();
	{
		if (fc_tstate->opaque != NULL)
			fc_routine->DestroyOpaque(fc_tstate);
		PG_RE_THROW();
	}
	PG_END_TRY();

	/* 清理并返回到原始内存上下文 */

	if (fc_tstate->opaque != NULL)
	{
		fc_routine->DestroyOpaque(fc_tstate);
		fc_tstate->opaque = NULL;
	}

	MemoryContextSwitchTo(fc_oldcxt);
	MemoryContextReset(fc_tstate->perTableCxt);
}

/*
 * 在表表达式构建器上下文中填充命名空间声明、行过滤器和列过滤器。
 */
static void fc_tfuncInitialize(TableFuncScanState *fc_tstate, ExprContext *fc_econtext, Datum fc_doc)
{
	const TableFuncRoutine *fc_routine = fc_tstate->routine;
	TupleDesc	fc_tupdesc;
	ListCell   *fc_lc1,
			   *fc_lc2;
	bool		fc_isnull;
	int			fc_colno;
	Datum		fc_value;
	int			fc_ordinalitycol =
	((TableFuncScan *) (fc_tstate->ss.ps.plan))->tablefunc->ordinalitycol;

	/*
	 * 将文档作为可能已经压缩的 Datum 安装到 tablefunc 中上下文。
	 */
	fc_routine->SetDocument(fc_tstate, fc_doc);

	/* 评估命名空间规范 */
	forboth(fc_lc1, fc_tstate->ns_uris, fc_lc2, fc_tstate->ns_names)
	{
		ExprState  *fc_expr = (ExprState *) lfirst(fc_lc1);
		String	   *fc_ns_node = lfirst_node(String, fc_lc2);
		char	   *fc_ns_uri;
		char	   *fc_ns_name;

		fc_value = ExecEvalExpr((ExprState *) fc_expr, fc_econtext, &fc_isnull);
		if (fc_isnull)
			ereport(ERROR,
					(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
					 errmsg("namespace URI must not be null")));
		fc_ns_uri = TextDatumGetCString(fc_value);

		/* DEFAULT 被传递给 SetNamespace 作为 NULL */
		fc_ns_name = fc_ns_node ? strVal(fc_ns_node) : NULL;

		fc_routine->SetNamespace(fc_tstate, fc_ns_name, fc_ns_uri);
	}

	/* 将行过滤器表达式安装到表构建器上下文 */
	fc_value = ExecEvalExpr(fc_tstate->rowexpr, fc_econtext, &fc_isnull);
	if (fc_isnull)
		ereport(ERROR,
				(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
				 errmsg("row filter expression must not be null")));

	fc_routine->SetRowFilter(fc_tstate, TextDatumGetCString(fc_value));

	/*
	 * 将列过滤器表达式安装到表构建器上下文。
	 * 如果给出了表达式，则使用该表达式; 否则列名本身就是列过滤器。
	 */
	fc_colno = 0;
	fc_tupdesc = fc_tstate->ss.ss_ScanTupleSlot->tts_tupleDescriptor;
	foreach(fc_lc1, fc_tstate->colexprs)
	{
		char	   *fc_colfilter;
		Form_pg_attribute fc_att = TupleDescAttr(fc_tupdesc, fc_colno);

		if (fc_colno != fc_ordinalitycol)
		{
			ExprState  *fc_colexpr = lfirst(fc_lc1);

			if (fc_colexpr != NULL)
			{
				fc_value = ExecEvalExpr(fc_colexpr, fc_econtext, &fc_isnull);
				if (fc_isnull)
					ereport(ERROR,
							(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
							 errmsg("column filter expression must not be null"),
							 errdetail("Filter for column \"%s\" is null.",
									   NameStr(fc_att->attname))));
				fc_colfilter = TextDatumGetCString(fc_value);
			}
			else
				fc_colfilter = NameStr(fc_att->attname);

			fc_routine->SetColumnFilter(fc_tstate, fc_colfilter, fc_colno);
		}

		fc_colno++;
	}
}

/*
 * 从 TableFunc 表构建器加载所有行到元组存储中。
 */
static void fc_tfuncLoadRows(TableFuncScanState *fc_tstate, ExprContext *fc_econtext)
{
	const TableFuncRoutine *fc_routine = fc_tstate->routine;
	TupleTableSlot *fc_slot = fc_tstate->ss.ss_ScanTupleSlot;
	TupleDesc	fc_tupdesc = fc_slot->tts_tupleDescriptor;
	Datum	   *fc_values = fc_slot->tts_values;
	bool	   *fc_nulls = fc_slot->tts_isnull;
	int			fc_natts = fc_tupdesc->natts;
	MemoryContext fc_oldcxt;
	int			fc_ordinalitycol;

	fc_ordinalitycol =
		((TableFuncScan *) (fc_tstate->ss.ps.plan))->tablefunc->ordinalitycol;

	/*
	 * 我们需要一个短暂的内存上下文，以便每次在循环中清理它，
	 * 以避免浪费空间。我们的默认每元组上下文
	 * 足以完成这个工作，因为在这个元组周期中我们还没有使用它。
	 */
	fc_oldcxt = MemoryContextSwitchTo(fc_econtext->ecxt_per_tuple_memory);

	/*
	 * 不断从表构建器请求行，直到没有为止。
	 */
	while (fc_routine->FetchRow(fc_tstate))
	{
		ListCell   *fc_cell = list_head(fc_tstate->coldefexprs);
		int			fc_colno;

		CHECK_FOR_INTERRUPTS();

		ExecClearTuple(fc_tstate->ss.ss_ScanTupleSlot);

		/*
		 * 获取此行的每列的值，将其安装到插槽中；然后将元组添加到元组存储中。
		 */
		for (fc_colno = 0; fc_colno < fc_natts; fc_colno++)
		{
			Form_pg_attribute fc_att = TupleDescAttr(fc_tupdesc, fc_colno);

			if (fc_colno == fc_ordinalitycol)
			{
				/* 序列号列的快速路径 */
				fc_values[fc_colno] = Int32GetDatum(fc_tstate->ordinal++);
				fc_nulls[fc_colno] = false;
			}
			else
			{
				bool		fc_isnull;

				fc_values[fc_colno] = fc_routine->GetValue(fc_tstate,
												  fc_colno,
												  fc_att->atttypid,
												  fc_att->atttypmod,
												  &fc_isnull);

				/* 没有值？如果有，评估并应用默认值 */
				if (fc_isnull && fc_cell != NULL)
				{
					ExprState  *fc_coldefexpr = (ExprState *) lfirst(fc_cell);

					if (fc_coldefexpr != NULL)
						fc_values[fc_colno] = ExecEvalExpr(fc_coldefexpr, fc_econtext,
													 &fc_isnull);
				}

				/* 验证可能的 NOT NULL 约束 */
				if (fc_isnull && bms_is_member(fc_colno, fc_tstate->notnulls))
					ereport(ERROR,
							(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
							 errmsg("null is not allowed in column \"%s\"",
									NameStr(fc_att->attname))));

				fc_nulls[fc_colno] = fc_isnull;
			}

			/* 超前默认表达式列表 */
			if (fc_cell != NULL)
				fc_cell = lnext(fc_tstate->coldefexprs, fc_cell);
		}

		tuplestore_putvalues(fc_tstate->tupstore, fc_tupdesc, fc_values, fc_nulls);

		MemoryContextReset(fc_econtext->ecxt_per_tuple_memory);
	}

	MemoryContextSwitchTo(fc_oldcxt);
}
